﻿\section{\oracle: .MSB-файлы\ESph{}\PTBRph{}\PLph{}\ITAph{}\DEph{}\NLph{}}
\myindex{\oracle}

\epigraph{Работая над решением задачи, всегда полезно знать ответ.}{Законы Мерфи, правило точности}

Это бинарный файл, содержащий сообщения об ошибках вместе с их номерами.

Давайте попробуем понять его формат и найти способ распаковать его.

В \oracle имеются файлы с сообщениями об ошибках в текстовом виде, так что мы можем сравнивать файлы:
текстовый и запакованный бинарный
\footnote{Текстовые файлы с открытым кодом в \oracle имеются не для каждого .MSB-файла, вот почему мы будем работать над его форматом}.

Это начало файла ORAUS.MSG без ненужных комментариев:

\begin{lstlisting}[caption=Начало файла ORAUS.MSG без комментариев]
00000, 00000, "normal, successful completion"
00001, 00000, "unique constraint (%s.%s) violated"
00017, 00000, "session requested to set trace event"
00018, 00000, "maximum number of sessions exceeded"
00019, 00000, "maximum number of session licenses exceeded"
00020, 00000, "maximum number of processes (%s) exceeded"
00021, 00000, "session attached to some other process; cannot switch session"
00022, 00000, "invalid session ID; access denied"
00023, 00000, "session references process private memory; cannot detach session"
00024, 00000, "logins from more than one process not allowed in single-process mode"
00025, 00000, "failed to allocate %s"
00026, 00000, "missing or invalid session ID"
00027, 00000, "cannot kill current session"
00028, 00000, "your session has been killed"
00029, 00000, "session is not a user session"
00030, 00000, "User session ID does not exist."
00031, 00000, "session marked for kill"
...
\end{lstlisting}

Первое число --- это код ошибки.
Второе это, вероятно, могут быть дополнительные флаги.

\clearpage
Давайте откроем бинарный файл ORAUS.MSB и найдем эти текстовые строки. 
И вот они:

\begin{figure}[H]
\centering
\myincludegraphics{ff/Oracle_MSB/1.png}
\caption{Hiew: первый блок}
\label{fig:oracle_MSB_1}
\end{figure}

Мы видим текстовые строки (включая те, с которых начинается файл ORAUS.MSG) перемежаемые с какими-то
бинарными значениями.
Мы можем довольно быстро обнаружить что главная часть бинарного файла поделена на блоки размером 0x200 (512) байт.

\clearpage
Посмотрим содержимое первого блока:

\begin{figure}[H]
\centering
\myincludegraphics{ff/Oracle_MSB/2.png}
\caption{Hiew: первый блок}
\label{fig:oracle_MSB_2}
\end{figure}

Мы видим тексты первых сообщений об ошибках.
Что мы видим еще, так это то, что здесь нет нулевых байтов между сообщениями.
Это значит, что это не оканчивающиеся нулем Си-строки.
Как следствие, длина каждого сообщения об ошибке должна быть как-то закодирована.
Попробуем также найти номера ошибок.
Файл ORAUS.MSG начинается с таких: 
0, 1, 17 (0x11), 18 (0x12), 19 (0x13), 20 (0x14), 21 (0x15), 22 (0x16), 23 (0x17), 24 (0x18)...
Найдем эти числа в начале блока и отметим их красными линиями.
Период между кодами ошибок 6 байт.
Это значит, здесь, наверное, 6 байт информации выделено для каждого сообщения об ошибке.

Первое 16-битное значение (здесь 0xA или 10) означает количество сообщений в блоке: это можно проверить глядя на другие блоки.

Действительно: сообщения об ошибках имеют произвольный размер. 
Некоторые длиннее, некоторые короче. 
Но длина блока всегда фиксирована, следовательно, никогда не знаешь, сколько сообщений можно запаковать
в каждый блок.

Как мы уже отметили, так как это не оканчивающиеся нулем Си-строки, длина строки должна быть закодирована где-то.%

Длина первой строки \q{normal, successful completion} это 
29 (0x1D) байт.
Длина второй строки \q{unique constraint (\%s.\%s) violated} 
это 34 (0x22) байт.

Мы не можем отыскать этих значений (0x1D или/и 0x22) в блоке.

А вот еще кое-что.
\oracle должен как-то определять позицию строки, которую он должен загрузить, верно?
Первая строка \q{normal, successful completion} начинается с позиции 0x1444 (если считать с начала бинарного файла) или с 0x44 (от начала блока).
Вторая строка \q{unique constraint (\%s.\%s) violated} 
начинается с позиции 0x1461 (от начала файла) или с 0x61 (считая с начала блока).
Эти числа (0x44 и 0x61) нам знакомы! 
Мы их можем легко отыскать в начале блока.

Так что, каждый 6-байтный блок это:

\begin{itemize}
\item 16-битный номер ошибки; 
\item 16-битный ноль (может быть, дополнительные флаги; 
\item 16-битная начальная позиция текстовой строки внутри текущего блока.
\end{itemize}

Мы можем быстро проверить остальные значения чтобы удостовериться в своей правоте.
И здесь еще последний \q{пустой} 6-байтный блок с нулевым номером ошибки и начальной позицией за последним
символом последнего сообщения об ошибке.
Может быть именно так и определяется длина сообщения?
Мы просто перебираем 6-байтные блоки в поисках нужного номера ошибки, затем
мы узнаем позицию текстовой строки, затем мы узнаем позицию следующей текстовой строки глядя на
следующий 6-байтный блок!
Так мы определяем границы строки!
Этот метод позволяет сэкономить место в файле не записывая длину строки!
Нельзя сказать, что экономия памяти большая, но это интересный трюк.

\clearpage
Вернемся к заголовку .MSB-файла:

\begin{figure}[H]
\centering
\myincludegraphics{ff/Oracle_MSB/3.png}
\caption{Hiew: заголовок файла}
\label{fig:oracle_MSB_3}
\end{figure}

Теперь мы можем быстро найти количество блоков (отмечено красным).
Проверяем другие .MSB-файлы и оказывается что это справедливо для всех.
Здесь есть много других значений, но мы не будем разбираться с ними, так как наша задача (утилита для распаковки) уже решена.

А если бы мы писали запаковщик .MSB-файлов, тогда нам наверное пришлось бы понять, зачем нужны остальные.

\clearpage
Тут еще есть таблица после заголовка, по всей видимости, содержащая 16-битные значения:

\begin{figure}[H]
\centering
\myincludegraphics{ff/Oracle_MSB/4.png}
\caption{Hiew: таблица last\_errnos}
\label{fig:oracle_MSB_4}
\end{figure}

Их длина может быть определена визуально (здесь нарисованы красные линии).

Когда мы сдампили эти значения, мы обнаружили, что каждое 16-битное число --- это последний код ошибки для каждого блока.%

Так вот как \oracle быстро находит сообщение об ошибке:

\begin{itemize}
\item загружает таблицу, которую мы назовем last\_errnos 
(содержащую последний номер ошибки для каждого блока);

\item находит блок содержащий код ошибки, полагая что все коды ошибок увеличиваются и внутри каждого блока
и также в файле;

\item загружает соответствующий блок;

\item перебирает 6-байтные структуры, пока не найдется соответствующий номер ошибки;

\item находит позицию первого символа из текущего 6-байтного блока;

\item находит позицию последнего символа из следующего 6-байтного блока;

\item загружает все символы сообщения в этих пределах.
\end{itemize}

Это программа на Си которую мы написали для распаковки .MSB-файлов:
\href{http://go.yurichev.com/17213}{beginners.re}.

И еще два файла которые были использованы в этом примере
 
(\oracle 11.1.0.6):
\href{http://go.yurichev.com/17214}{beginners.re},
\href{http://go.yurichev.com/17215}{beginners.re}.

\subsection{Вывод}

Этот метод, наверное, слишком олд-скульный для современных компьютеров.
Возможно, формат этого файла был разработан в середине 1980-х кем-то, кто программировал для мейнфреймов,
учитывая экономию памяти и места на дисках.
Тем не менее, это интересная (хотя и простая) задача на разбор проприетарного формата файла без
заглядывания в код \oracle.
